﻿/*
 * Created by SharpDevelop.
 * User: developer
 * Date: 10/27/2010
 * Time: 9:06 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Xml;

namespace xmldict {
	using xmldict;
	/// <summary>
	/// Description of XMLUtils.
	/// </summary>
	public class XMLUtils {
		public static string VALUE_KEY="@";
		
		private XMLUtils() {}

		private static string __toString(object data){
			if (data == null){
				return null;
			}
			else {
				return data.ToString();
			}
		}
		private static void _AppendTextNode(XmlElement element, object data){
			if (data == null){
				return;
			}
			
			string text = __toString(data).Trim();
			if (text.Length > 0){
				element.InnerText = text;
			}
		}
#region Write dictionary		
		public static void WriteAttributes(XmlDocument document, XmlElement current, object source){
			object at_str = null;

			if (Utils.isList(source)){
				throw new ArgumentException("Attributes from list are not supported");
			}

			if (Utils.isDict(source)){
				IMinimalDict dict = Utils.asDict(source);
				if (dict.ContainsKey(VALUE_KEY)){
					at_str = dict[VALUE_KEY];
				}
				foreach(string key in dict.Keys){
					if(key != VALUE_KEY){
						current.SetAttribute(key, __toString(dict[key]));
					}
				}
			} else { 
				at_str = source;
			}
			
			if(at_str != null){
				_AppendTextNode(current, at_str);
			}
		}
		public static void WriteList(XmlDocument document, XmlElement current, IMinimalList source, string key){
			int len = source.Count;
			for (int i=0;i<len;i++){
				object data = source[i];
				if (Utils.isList(data)){
					throw new ArgumentException("Lists inside lists are not supported");
				}
				XmlElement e = document.CreateElement(key);
				current.AppendChild(e);
				if (Utils.isDict(data)){
					IMinimalDict dict = Utils.asDict (data);
					WriteDict(document, e, dict);
				} else{
					_AppendTextNode(e, data);
				}
			}
		}
		public static void WriteDict(XmlDocument document, XmlElement current, IMinimalDict source) {
			if (source == null){
				throw new ArgumentNullException("Source dictionary must not be null");
			}
			
			if (source.ContainsKey(VALUE_KEY)) {
				WriteAttributes(document, current, source[VALUE_KEY]);
			}
			
			foreach (string key in source.Keys){
				if (key == VALUE_KEY) {
					continue;
				}
				
				object data = source[key];

				if (data == null) {
					continue;
				}

				if (Utils.isList(data)){
					WriteList(document, current,Utils.asList(data), key);
					continue;
				}

				XmlElement dElement = document.CreateElement(key);
				current.AppendChild(dElement);
				if (Utils.isDict(data)) {
					WriteDict(document, dElement, Utils.asDict(data));
				} else {
					// simple element
					_AppendTextNode(dElement, data);
				}
			}
		}

#endregion
		public static void ReadAttrs(XmlElement current, IMinimalDict dest){
			if (!current.HasAttributes ){
				return;
			} 
			IMinimalDict at;

			dest.CreateValue(VALUE_KEY);

			if (dest.ContainsKey(VALUE_KEY)){
				at = Utils.asDict(dest[VALUE_KEY]);
			} else {
				dest[VALUE_KEY] = new DictAdapter();
				at = Utils.asDict(dest[VALUE_KEY]);
			}
			
			foreach(XmlAttribute attr in current.Attributes){
				at[attr.Name] = attr.InnerText;
			}
		}
		private static bool hasChildren(XmlElement element){
			foreach (XmlNode n in element){
				if (n.NodeType == XmlNodeType.Element){
					return true;
				}
			}
			return false;
		}
		public static void ReadDict(XmlElement current, IMinimalDict dest){
			if(dest == null){
				throw new ArgumentNullException("target dict is null");
			} 
			if (!Utils.isDict(dest)){
				throw new ArgumentException("target dict is not dict");
			}
			ReadAttrs(current,dest);

			List<XmlElement> children = new List<XmlElement>();
			string text = "";
			foreach(XmlNode element in current){
				if (element.NodeType == XmlNodeType.Element) {
					children.Add(element as XmlElement);
				} else {
					if (element.NodeType == XmlNodeType.Text ||
							element.NodeType == XmlNodeType.CDATA){
						text += element.InnerText;
					}
				}
			}
			if (text.Length > 0){
				dest.CreateValue(VALUE_KEY);
				if (dest.ContainsKey(VALUE_KEY)){
					object at_val = dest[VALUE_KEY];
					if (Utils.isList(at_val)){
						throw new ArgumentException("list in @?");
					}
					if (Utils.isDict(at_val)){
						Utils.asDict(at_val)[VALUE_KEY] = text;
					} else {
						throw new ArgumentException("don't know what to do at this point");
					}
				} else {
					 dest [VALUE_KEY] = text; 
				}
			}

			foreach(XmlElement element in children){
				string key = element.Name;
				object dval;

				dest.CreateValue(key); // ensure that value is created for this key
				if (dest.ContainsKey(key)) {
					dval = dest[key];
				}else {
					dval = null;
				}

				if (hasChildren(element) ||  element.HasAttributes) {

					if (dval == null) {
						dest[key] = new DictAdapter();
						ReadDict(element, Utils.asDict(dest[key]));
						continue;
					}
					IMinimalList list;
					if (Utils.isList(dval)) {
						list = Utils.asList(dval);
					} else {
						dest[key] = new ListAdapter();
						list = Utils.asList(dest[key]);
						list.Add(dval);
					}
					IMinimalDict a = new DictAdapter();
					list.Add(a);
					ReadDict(element, a);
				} else {
					string chtext = "";
					foreach(XmlNode e in element){
						if (e.NodeType == XmlNodeType.Text ||
								e.NodeType == XmlNodeType.CDATA){
							chtext += e.InnerText;
						}
					}
					if (chtext.Length == 0) {
						continue;
					}
					if (dval == null) {
						dest[key] = chtext;
						continue;
					} 
					IMinimalList list;
					if(Utils.isList(dval)){
						list = Utils.asList(dval);
					} else {
						dest[key] = new ListAdapter();
					    list = Utils.asList(dest[key]);
						list.Add(dval);
					}
					list.Add(chtext);
				}
			}
		}
	}
}
